package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import model.Player;
import net.beadsproject.beads.core.Bead;
import net.beadsproject.beads.ugens.Clock;
import view.EditorPanel;
import view.GUI;
import view.MenuPanel;
import view.PlaybackState;

/**
 * Responsible for keeping the Player and the GUI in sync.<br>
 * This included updating the Seeker's location at every beat and modifying the
 * current time of the player to different times.
 */
public class PlayerControlListener implements ActionListener, ChangeListener {

	//ActionCommands this listener can handle
	public static final String PLAY = "PLAY", PAUSE = "PAUSE", STOP = "STOP";

	private Player player;
	private MenuPanel menuPanel;
	private EditorPanel editorPanel;
	private JSlider seeker;
	private Clock clock;

	public PlayerControlListener(Player player, GUI gui) {
		this.editorPanel = gui.getEditorPanel();
		this.clock = player.getClock();
		//Update the seeker at every beat
		this.clock.addMessageListener(new Bead(){
			@Override
			protected void messageReceived(Bead message) {
				//Make sure changes are in the AWT EventDispatch Thread to avoid
				//conflicted updates.
				SwingUtilities.invokeLater(new SeekerUpdater(message == clock));
			}
		});
		this.player = player;
		this.menuPanel = gui.getMenuPanel();
		this.seeker = gui.getEditorPanel().getSeeker();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();

		if (command.equals(PLAY)) {
			if (player.isPlaying()) {
				pausePlayer(false);	//Pause if already playing
			} else {
				playPlayer();
			}
		} else if (command.equals(PAUSE)) {
			pausePlayer(false);
		} else if (command.equals(STOP)) {
			pausePlayer(true);
		}
	}

	@Override
	public void stateChanged(ChangeEvent e) {
		//Seeker was adjusted, set the player's time to it.
		player.setTime(seeker.getValue());
	}

	private void pausePlayer(boolean stop) {
		if (stop) {
			player.stop();	//Set the time to zero too.
		} else {
			player.pause();
		}

		menuPanel.setState(PlaybackState.PAUSE);
	}

	private void playPlayer() {
		player.startPlaying();
		menuPanel.setState(PlaybackState.PLAY);
	}

	private class SeekerUpdater implements Runnable {
		//Whether or not a call to this will increment the Player's time.
		private boolean increment;

		private SeekerUpdater(boolean increment) {
			this.increment = increment;
		}

		@Override
		public void run() {
			//Temporarily stop listening to adjust the seeker without firing events.
			seeker.removeChangeListener(PlayerControlListener.this);
			seeker.setValue(player.getTime());
			seeker.addChangeListener(PlayerControlListener.this);

			editorPanel.highlightColumn(player.getTime()); //Hightlight the column

			if (increment && player.isPlaying() && !seeker.getValueIsAdjusting()) {
				player.incrementTime();
			}
			
			editorPanel.repaint();
		}
	}

}
